<script lang="tsx">
import { defineComponent, ref, provide, withKeys, nextTick, watch, onBeforeUpdate } from 'vue';
import {
  ElIcon,
  ElCol,
  ElInput,
  ElButton,
  ElSelect,
  ElCalendar,
  ElDatePicker,
  ElRow,
  ElFormItem,
  ElForm,
} from 'element-plus';
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { InputNumber } from '@/components/InputNumber';
import { IConfigItem, searchAreaProps, ButtonPositionEnum } from './types';
import { useShowLength } from './hooks/use-show-length';

export default defineComponent({
  name: 'SearchArea',
  components: {
    ArrowDown,
    ArrowUp,
    ElIcon,
    ElInput,
    ElButton,
    ElSelect,
    ElCalendar,
    ElDatePicker,
    ElCol,
    ElRow,
    ElFormItem,
    ElForm,
    InputNumber,
  },
  props: searchAreaProps,
  emits: ['handleSearch', 'update:modelValue', 'handleReset'],
  setup(props, { emit }) {
    // 标记
    provide<boolean>('searchArea', true);
    // 控制展开收起
    const isOpen = ref(false);
    // 高级筛选
    const openFilter = ref(false);
    // 存放日期(可能有多个，保证每个都是唯一)
    const timeRange = ref<{
      [key: string]: string[];
    }>({});

    const globalCols = Object.assign({ xs: 24, sm: 12, md: 12, lg: 8, xl: 6 }, props.cols || {});

    // 默认值
    const buttonPositionMode = props.buttonPosition;

    const marginBottom = props.marginBottom;

    const { showLength } = useShowLength({
      buttonPositionMode,
      globalCols,
      config: props.config,
    });

    const setDateRange = () => {
      props.config.forEach((item) => {
        if (item?.component === 'datePicker') {
          let range: string[] = [];
          if (props.modelValue[(item?.prop as string[])?.[0] ?? '']) {
            range = [
              props.modelValue[(item?.prop as string[])?.[0] ?? ''],
              props.modelValue[(item?.prop as string[])?.[1] ?? ''],
            ];
          }
          timeRange.value[(item?.prop as string[])?.[0] ?? ''] = range;
        }
      });
    };

    watch(
      () => props.modelValue,
      () => {
        setDateRange();
      },
      {
        deep: true,
        immediate: true,
      }
    );

    // 点击展开/收起
    const handleOpenRetract = () => {
      const resizeEvent = new Event('resize');
      window.dispatchEvent(resizeEvent);
      isOpen.value = !isOpen.value;
    };
    const handleOpenFilter = () => {
      openFilter.value = !openFilter.value;
    };
    // 更新值
    const updateValue = (value: string | string[], item: IConfigItem) => {
      if (item.component === 'slot') return;
      if (['input', 'select', 'inputNumber'].includes(item.component!)) {
        emit('update:modelValue', {
          ...props.modelValue,
          [item.prop as string]: value,
        });
      } else if (item.component === 'datePicker') {
        emit('update:modelValue', {
          ...props.modelValue,
          [(item.prop as string[])?.[0]]: value?.[0] ?? '',
          [(item.prop as string[])?.[1]]: value?.[1] ?? '',
        });
      }
    };

    const handleReset = async () => {
      // 重置日期
      Object.keys(timeRange.value).forEach((key) => {
        timeRange.value[key] = [];
      });

      await nextTick(); // 需要等到emit执行完
      emit('handleReset');
    };

    return {
      globalCols,
      marginBottom,
      buttonPositionMode,
      showLength,
      isOpen,
      openFilter,
      defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)],
      timeRange,
      handleReset,
      handleOpenRetract,
      handleOpenFilter,
      updateValue,
    };
  },
  render() {
    const renderComponent = (item: IConfigItem) => {
      const { label, component, prop, ...restProps } = item;
      switch (component) {
        case 'slot':
          return this.$slots?.[prop]?.({ data: this.modelValue, prop: item.prop });
        case 'input': {
          return (
            <el-input
              placeholder="请输入"
              clearable={item?.clearable ?? true}
              {...restProps}
              v-model={this.modelValue[item.prop]}
              onInput={(value: string) => {
                const _value = value.trim();
                this.updateValue(_value, item);
              }}
            />
          );
        }
        case 'inputNumber': {
          return (
            <input-number
              placeholder="请输入"
              clearable={item?.clearable ?? true}
              {...restProps}
              v-model={this.modelValue[item.prop]}
              onInput={(value: string) => {
                const _value = value.trim();
                this.updateValue(_value, item);
              }}
            />
          );
        }
        case 'select': {
          return (
            <el-select
              placeholder="请选择"
              style={{ width: '100%' }}
              class="input-with-select"
              clearable={item?.clearable ?? true}
              {...restProps}
              v-model={this.modelValue[item.prop]}
              onChange={(value: string) => {
                this.updateValue(value, item);
              }}
            >
              {item.options?.map((it) => {
                return <el-option label={it[item.optionLabel ?? 'label']} value={it[item.optionValue ?? 'value']} />;
              })}
            </el-select>
          );
        }
        case 'datePicker': {
          return (
            <el-date-picker
              range-separator="至"
              type="daterange"
              clearable
              value-format="YYYY-MM-DD HH:mm:ss"
              start-placeholder={item?.placeholder?.[0] ?? '开始日期'}
              end-placeholder={item?.placeholder?.[1] ?? '结束日期'}
              default-time={this.defaultTime}
              {...restProps}
              v-model={this.timeRange[item?.prop?.[0] ?? '']}
              onChange={(value: string[]) => {
                this.updateValue(value, item);
              }}
            />
          );
        }
        default:
          return null;
      }
    };
    const handleSubmit = (e: Event) => {
      const event = e || window.event;
      if (event.preventDefault) {
        event.preventDefault();
      } else {
        window.event?.preventDefault();
      }
    };
    const renderBtns = () => {
      return (
        <div class="btns" style={{ 'margin-bottom': this.marginBottom }}>
          {this.$slots?.prependButton?.()}
          <el-button
            type="primary"
            icon="search"
            onClick={() => {
              this.$emit('handleSearch');
            }}
          >
            查询
          </el-button>
          <el-button plain type="success" icon="refresh" onClick={this.handleReset}>
            重置
          </el-button>
          {this.$slots?.appendButton?.()}
          {this.config.length > this.showLength && this.showFilter && (
            <el-button class="ml-12" onClick={this.handleOpenFilter}>
              筛选
              <el-icon class="icon">{this.openFilter ? <arrow-left /> : <arrow-right />}</el-icon>
            </el-button>
          )}
          {this.config.length > this.showLength && !this.showFilter && (
            <el-button class="ml-12" onClick={this.handleOpenRetract}>
              {this.isOpen ? '收起' : '展开'}
              <el-icon class="icon">{this.isOpen ? <arrow-up /> : <arrow-down />}</el-icon>
            </el-button>
          )}
        </div>
      );
    };
    const renderFilterBtns = () => {
      return (
        <div class="btns flex-end" style={{ 'margin-bottom': this.marginBottom }}>
          {this.$slots?.prependButton?.()}
          <el-button
            type="primary"
            icon="search"
            onClick={() => {
              this.openFilter = false;
              this.$emit('handleSearch');
            }}
          >
            查询
          </el-button>
          <el-button
            plain
            type="success"
            icon="refresh"
            onClick={() => {
              this.openFilter = false;
              this.handleReset();
            }}
          >
            重置
          </el-button>
          {this.$slots?.appendButton?.()}
        </div>
      );
    };
    return (
      <div class="search-area-wrap">
        <>
          <el-form
            class="form"
            label-width={this.labelWidth}
            onKeyup={withKeys(() => this.$emit('handleSearch'), ['enter'])}
            onSubmit={(e: Event) => handleSubmit(e)}
          >
            <el-row>
              {this.config
                .filter((item) => !item.isHidden)
                .map((item: IConfigItem, index: number) => {
                  const cols = Object.assign({}, this.globalCols, item.cols || {});
                  return (
                    <>
                      <el-col {...cols} v-show={index < this.showLength || (index >= this.showLength && this.isOpen)}>
                        <el-form-item
                          label={item.label}
                          label-width={item.labelWidth || this.labelWidth}
                          style={{ marginBottom: this.marginBottom }}
                        >
                          {renderComponent(item)}
                        </el-form-item>
                      </el-col>
                      {this.buttonPositionMode === ButtonPositionEnum.IN && index === this.showLength - 1 ? (
                        <el-col {...this.globalCols}>{renderBtns()}</el-col>
                      ) : null}
                    </>
                  );
                })}

              {this.buttonPositionMode === ButtonPositionEnum.LAST ? (
                <el-col {...this.globalCols}>{renderBtns()}</el-col>
              ) : null}
            </el-row>
          </el-form>
          {this.buttonPositionMode === ButtonPositionEnum.OUT && renderBtns()}
        </>
        <el-drawer v-model={this.openFilter} title="高级筛选">
          <el-form
            class="form"
            label-width={this.labelWidth}
            onKeyup={withKeys(() => {
              this.openFilter = false;
              return this.$emit('handleSearch');
            }, ['enter'])}
            onSubmit={(e: Event) => handleSubmit(e)}
          >
            {this.config
              .filter((item) => !item.isHidden)
              .map((item: IConfigItem, index: number) => {
                return (
                  <el-form-item
                    label={item.label}
                    label-width={item.labelWidth || this.labelWidth}
                    style={{ marginBottom: this.marginBottom }}
                  >
                    {renderComponent(item)}
                  </el-form-item>
                );
              })}

            {renderFilterBtns()}
          </el-form>
        </el-drawer>
      </div>
    );
  },
});
</script>

<style lang="scss" scoped>
.search-area-wrap {
  position: relative;
  display: flex;
  width: 100%;
  padding-right: 14px;

  .input-with-select {
    &.pl-10 {
      padding-left: 10px;
    }

    :deep(.el-select) {
      .el-input__inner {
        width: 100%;
      }
    }
  }

  .form {
    flex: 1;
  }

  .btns {
    display: flex;
    flex-wrap: wrap;
    margin-left: 25px;

    .ml-12 {
      margin-left: 12px;
    }

    .icon {
      margin-left: 5px;
    }
  }

  .flex-end {
    justify-content: flex-end;
  }
}
</style>
